
package com.huaweicloud.haydn.delivercore.agent.service.metadata.impl;

import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DOT_FILETYPE_CSV;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.FILETYPE_ZIP;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.SYS_URL;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_DB;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_EMPTY;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_FIELD;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_LOG;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_TABLE;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.huaweicloud.haydn.delivercore.agent.dto.excel.DbExcelDto;
import com.huaweicloud.haydn.delivercore.agent.dto.excel.DbLogExcelDto;
import com.huaweicloud.haydn.delivercore.agent.dto.excel.FieldEmptyExcelDto;
import com.huaweicloud.haydn.delivercore.agent.dto.excel.FieldExcelDto;
import com.huaweicloud.haydn.delivercore.agent.dto.excel.TableExcelDto;
import com.huaweicloud.haydn.delivercore.agent.service.metadata.DatabaseUtil;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipParameters;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @since 2022-09-26
 */
@Service
@Slf4j
public class AgentExecUtil {

    public void genDbExcelDto(DatabaseUtil dbUtil, String datalinkName, String dbNames, String schemaNames,
        String idx) {
        SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
        String today = ymd.format(new Date());
        List<DbExcelDto> allDbs = dbUtil.getAllDbs(datalinkName, dbNames, schemaNames);
        allDbs.forEach(dto -> dto.setGenTime(today));
        HashSet<DbExcelDto> data = new HashSet<>(allDbs);
        exportDataToZipFile(idx, TYPE_DB, data, DbExcelDto.class);
    }

    public void genDbLog(DatabaseUtil dbUtil, String datalinkName, String idx) {
        List<DbLogExcelDto> dbLogExcelDtos = dbUtil.genLogs();
        exportDataToZipFile(idx, TYPE_LOG, dbLogExcelDtos, DbLogExcelDto.class);
    }

    public void genTableExcelDto(DatabaseUtil dbUtil, String datalinkName, String dbNames, String schemaNames,
        String tableNames, String idx) {
        SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
        String today = ymd.format(new Date());
        List<TableExcelDto> allTables = dbUtil.getAllTables(datalinkName, dbNames, schemaNames, tableNames);
        allTables.forEach(dto -> dto.setGenTime(today));
        HashSet<TableExcelDto> tableExcelDtos = new HashSet<>(allTables);

        exportDataToZipFile(idx, TYPE_TABLE, tableExcelDtos, TableExcelDto.class);
    }

    public void genFieldExcelDto(DatabaseUtil dbUtil, String datalinkName, String dbNames, String schemaNames,
        String tableNames, String idx) {
        SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
        String today = ymd.format(new Date());
        List<FieldExcelDto> allFields = dbUtil.getAllFields(datalinkName, dbNames, schemaNames, tableNames);
        List<String> pkFieldList = new ArrayList<>();
        if (CollectionUtils.isEmpty(allFields)) {
            log.info("empty allFields");
        }
        allFields.forEach(dto -> {
            dto.setGenTime(today);
            if ("YES".equals(dto.getIsPrimaryKey())) {
                pkFieldList.add(dto.getDatalinkName() + dto.getDatabaseName()
                    + Optional.ofNullable(dto.getSchemaName()).orElse("") + dto.getTableName() + dto.getFieldNameEn());
            }
        });
        HashSet<FieldExcelDto> fieldExcelDtos = new HashSet<>(allFields);
        fieldExcelDtos.removeIf(dto -> pkFieldList
            .contains(dto.getDatalinkName() + dto.getDatabaseName()
                + Optional.ofNullable(dto.getSchemaName()).orElse("") + dto.getTableName() + dto.getFieldNameEn())
            && !"YES".equals(dto.getIsPrimaryKey()));

        exportDataToZipFile(idx, TYPE_FIELD, fieldExcelDtos, FieldExcelDto.class);
    }

    public void genEmptyRateExcelDto(DatabaseUtil dbUtil, String datalinkName, String dbNames, String schemaNames,
        String tableNames, String idx, Map<String, List<String>> fieldTableMap) {
        List<FieldEmptyExcelDto> allEmptyRateFields =
            dbUtil.getAllEmptyRateFields(datalinkName, dbNames, schemaNames, fieldTableMap);
        HashSet<FieldEmptyExcelDto> fieldEmptyExcelDtos = new HashSet<>(allEmptyRateFields);

        exportDataToZipFile(idx, TYPE_EMPTY, fieldEmptyExcelDtos, FieldEmptyExcelDto.class);
    }

    /**
     * export data to csv file and export to zipFile
     *
     * @param idx
     * @param dataList data
     * @param clazz csv template class for data
     */
    private void exportDataToZipFile(String idx, String type, Collection dataList, Class<?> clazz) {
        // csv file name rule: idx_type_date.csv
        String csvFileName =
            idx + "_" + type + "_" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + DOT_FILETYPE_CSV;

        // if dataList is empty,skip generate the csv file for this idx
        if (CollectionUtils.isEmpty(dataList)) {
            log.info("no." + idx + " " + type + " no data find");
            return;
        }

        // export to csv file
        try (ExcelWriter excelWriter = EasyExcel.write(csvFileName, clazz).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet").build();
            excelWriter.write(dataList, writeSheet);
        }
        log.info("export data to " + csvFileName + "; the size of data: " + dataList.size());

        // add csv file to zip,and delete csv file
        File csvFile = new File(System.getProperty(SYS_URL) + File.separator + csvFileName);
        try {
            File zipFile = new File(System.getProperty(SYS_URL), "result." + FILETYPE_ZIP);
            if (!zipFile.exists()) {
                try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) {
                    ZipEntry zipEntry = new ZipEntry(csvFileName);
                    zipOut.putNextEntry(zipEntry);
                    try (FileInputStream fileInputStream =
                        new FileInputStream(System.getProperty(SYS_URL) + File.separator + csvFileName)) {
                        IOUtils.copy(fileInputStream, zipOut);
                        zipOut.closeEntry();
                    }
                }
            } else {
                ZipFile zip = new ZipFile(zipFile);
                ZipParameters parameters = new ZipParameters();
                zip.addFile(new File(System.getProperty(SYS_URL) + File.separator + csvFileName), parameters);
                removeOutdateFile(zip);
            }
        } catch (IOException ioe) {
            log.error(ioe.getMessage());
        } finally {
            FileUtils.deleteQuietly(csvFile);
        }
    }

    @SneakyThrows
    private void removeOutdateFile(ZipFile zip) {
        SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE, -30);
        Date time = calendar.getTime();
        String timeStr = ymd.format(time);
        String dbName = TYPE_DB + "_" + timeStr + DOT_FILETYPE_CSV;
        String tableName = TYPE_TABLE + "_" + timeStr + DOT_FILETYPE_CSV;
        String fieldName = TYPE_FIELD + "_" + timeStr + DOT_FILETYPE_CSV;
        List<FileHeader> fileHeaders = zip.getFileHeaders();
        for (FileHeader fileHeader : fileHeaders) {
            if (dbName.equals(fileHeader.getFileName()) || tableName.equals(fileHeader.getFileName())
                || fieldName.equals(fileHeader.getFileName())) {
                zip.removeFile(fileHeader);
            }
        }
    }

}
